package home

import (
	"adai.design/homeserver/home/model"
	"adai.design/homeserver/home/data"
	"fmt"
	"encoding/json"
	"adai.design/homeserver/devices"
	"adai.design/homeserver/log"
	"adai.design/homeserver/members"
	"adai.design/homeserver/db"
	"time"
)

type Container struct {
	Id 				string          `json:"id" bson:"id"`
	Version 		int             `json:"version" bson:"version"`
	IsReachable		bool			`json:"reachable" bson:"reachable"`
	Accessories []*model.Accessory 	`json:"accessories" bson:"accessories"`
}

func (c *Container) getCharacteristicType(char *data.Characteristic) string {
	for _, a := range c.Accessories {
		if a.UUID == char.AId {
			for _, s := range a.Services {
				if s.ID == char.SId {
					for _, c := range s.Characteristics {
						if c.Id == char.CId {
							return c.Type
						}
					}
				}
			}
		}
	}
	return ""
}

func (c *Container) characteristicUpdateMark(h *Home, chars ...*data.Characteristic)  {
	for _, char := range chars {
		switch c.getCharacteristicType(char) {
		case model.TypeCurrentTemperature, model.TypeCurrentRelativeHumidity, model.TypeCurrentAmbientLightLevel,
			model.TypeOn, model.TypeContactSensorState, model.TypeTargetTemperature, model.TypeAirConditionerMode,
			model.TypeAirConditionerRotationMode, model.TypeAirConditionerWindSpeed, model.TypeScreenBrightness:

			db.HomeCharacteristicMark(&db.HomeCharacteristicInfo{
				HomeId: h.Id,
				AccessoryId: char.AId,
				ServiceId: char.SId,
				CharacteristicId: char.CId,
				Value: char.Value,
				Time: time.Now(),
			})
		default:
			break
		}
	}
}

// 根据获取的属性值刷新容器
func (c *Container) refreshByCharacteristic(chars ...*data.Characteristic) error {
	c.IsReachable = true
	for _, char := range chars {
		for _, a := range c.Accessories {
			if a.UUID == char.AId {
				a.RefreshCharacteristic(char)
			}
		}
	}
	return nil
}

// 刷新某一属性的值
func (c *Container) refreshCharacteristic(chars ...*data.Characteristic) error {
	for _, char := range chars {
		for _, a := range c.Accessories {
			if a.UUID == char.AId {
				a.RefreshCharacteristic(char)
			}
		}
	}
	return nil
}

func (c *Container) getCharacteristic() []*data.Characteristic {
	var chars []*data.Characteristic
	for _, a := range c.Accessories {
		if a.IsReachable {
			if cs := a.GetCharacteristics(); cs != nil {
				chars = append(chars, cs...)
			}
		}
	}
	return chars
}

type ContainerManager struct {
	containers 		[]*Container
}

func (cs *ContainerManager) getCharacteristic(aid string, sid, cid int) *model.Characteristic {
	for _, c := range cs.containers {
		for _, a := range c.Accessories {
			if c.Id == aid {
				for _, s := range a.Services {
					if s.ID == sid {
						for _, c := range s.Characteristics {
							if c.Id == cid {
								return c
							}
						}
					}
				}
			}
		}
	}
	return nil
}

func (cs *ContainerManager) findContainerByAccessoryId(id string) *Container {
	for _, c := range cs.containers {
		for _, a := range c.Accessories {
			if a.UUID == id {
				return c
			}
		}
	}
	return nil
}

func (cs *ContainerManager) findContainerById(id string) *Container {
	for _, c := range cs.containers {
		if c.Id == id {
			return c
		}
	}
	return nil
}

// 执行动作
func (cs *ContainerManager) executeActions(h *Home, as ...*Action) error {
	var err error
	group := make(map[string][]*data.Characteristic)

	for _, a := range as {
		db.HomeActionsMark(&db.HomeActionInfo{
			HomeId: h.Id,
			AccessoryId: a.AId,
			ServiceId: a.SId,
			CharacteristicId: a.CId,
			Value: a.Value,
			Time: time.Now(),
		})
	}

	// 将动作根据执行的设备进行分组(不同的wifi终端设备)
	for _, a := range as {
		container := cs.findContainerByAccessoryId(a.AId)
		if container == nil || container.IsReachable != true {
			err = fmt.Errorf("container(%s) unreachable", a.AId)
			continue
		}

		c := &data.Characteristic{
			AId:   a.AId,
			SId:   a.SId,
			CId:   a.CId,
			Value: a.Value,
		}

		if g, ok := group[container.Id]; ok {
			group[container.Id] = append(g, c)
		} else {
			group[container.Id] = []*data.Characteristic{c}
		}
	}

	// 将消息发送给不同的wifi设备去执行
	for k, v := range group {
		buf, _ := json.Marshal(v)
		msg := &devices.Message{
			Path: devices.MsgPathCharacteristic,
			Method: devices.MsgMethodPost,
			Data: buf,
		}
		h.postMessageToContainer(k, msg)
	}
	return err
}

// 处理设备发送过来的消息
func (cs *ContainerManager) handlePackage(h *Home, id string, msg *devices.Message) error {
	if msg.Path == devices.MsgPathContainer {
		// 设备在线\离线\添加\删除\版本更新
		if msg.Method == devices.MsgMethodPut {
			switch msg.State {
			case devices.ContainerStateOnline:
				req := &devices.Message{
					Path: devices.MsgPathContainer,
					Method: devices.MsgMethodGet,
				}
				h.postMessageToContainer(id, req)
				return nil

			case devices.ContainerStateOffline:
				container := cs.findContainerById(id)
				if container != nil {
					container.IsReachable = false
					for _, a := range container.Accessories {
						a.IsReachable = false
					}
				}

				// 通知用户
				cchars := &data.ContainerCharacteristic{
					Id: id,
					IsReachable: false,
				}

				buf, _ := json.Marshal(cchars)
				put := &members.Message{
					Path: PathContainerCharacteristic,
					Method: members.MsgMethodPut,
					Data: buf,
				}
				return h.putMessageToMember(put)


			case devices.ContainerStateAdd:
			case devices.ContainerStateDel:
			case devices.ContainerStateUpdate:
			}
			return nil
		}

		// 设备容器版本信息获取返回
		if msg.Method == devices.MsgMethodGet {
			info := struct {
				Version int `json:"version"`
			}{}

			if err := json.Unmarshal(msg.Data, &info); err == nil {
				log.Debug("container: %s", id)
				container := cs.findContainerById(id)
				if container != nil {
					log.Debug("container(%s) version(%d:%d)", id, info.Version, container.Version)
				} else {
					log.Debug("container(%s) version(%d)", id, info.Version)
				}

				if container == nil || container.Version < info.Version {
					msg := &devices.Message{
						Path:   devices.MsgPathAccessories,
						Method: devices.MsgMethodGet,
					}
					h.postMessageToContainer(id, msg)
				} else {
					msg := &devices.Message{
						Path:   devices.MsgPathCharacteristic,
						Method: devices.MsgMethodGet,
					}
					h.postMessageToContainer(id, msg)
				}

			}
		}
	}

	// 容器信息获取
	if msg.Path == devices.MsgPathAccessories {
		if msg.Method == devices.MsgMethodGet {

		}
	}

	// 设备属性状态信息
	if msg.Path == devices.MsgPathCharacteristic {
		// 获取返回结果
		if msg.Method == devices.MsgMethodGet {
			var chars []*data.Characteristic
			if err := json.Unmarshal(msg.Data, &chars); err != nil {
				log.Error("%s", err)
				return err
			}

			// 更新属性
			container := cs.findContainerById(id)
			if container == nil {
				return fmt.Errorf("container(%s) 404 not found", id)
			}
			for _, a := range container.Accessories {
				a.SetReachable(false)
			}
			container.refreshByCharacteristic(chars...)
			container.characteristicUpdateMark(h, chars...)

			// 通知用户
			cchars := &data.ContainerCharacteristic{
				Id: id,
				IsReachable: true,
				Version: container.Version,
				Chars: container.getCharacteristic(),
			}

			buf, _ := json.Marshal(cchars)
			put := &members.Message{
				Path: PathContainerCharacteristic,
				Method: members.MsgMethodPut,
				Data: buf,
			}
			return h.putMessageToMember(put)
		}

		// 主动上传返回结果
		if msg.Method == devices.MsgMethodPut {
			var chars []*data.Characteristic
			if err := json.Unmarshal(msg.Data, &chars); err != nil {
				log.Error("%s", err)
				return err
			}

			// 更新属性
			container := cs.findContainerById(id)
			if container == nil {
				return fmt.Errorf("container(%s) 404 not found", id)
			}
			container.refreshByCharacteristic(chars...)
			container.characteristicUpdateMark(h, chars...)

			// 通知家庭
			h.refreshCharacteristic(id, chars...)
		}
	}
	return nil
}

// 获取容器全部信息
func (cs *ContainerManager) handleMemberContainer(h *Home, pkg *members.MessagePkg) error {
	msg := pkg.Msg
	if msg.Method == members.MsgMethodGet {
		buf, _ := json.Marshal(cs.containers)
		ack := &members.MessagePkg{
			Type: pkg.Type,
			Ctx: pkg.Ctx,
			Msg: &members.Message{
				Path: msg.Path,
				Method: members.MsgMethodGet,
				State: "ok",
				Home: h.Id,
				Data: buf,
			},
		}
		h.replyMemberResult(ack)
	}
	return nil
}

// 获取容器属性信息
func (cs *ContainerManager) handleMemberContainerCharacteristic(h *Home, pkg *members.MessagePkg) error {
	msg := pkg.Msg
	if msg.Method == members.MsgMethodGet {
		var ccs  []*data.ContainerCharacteristic
		for _, container := range cs.containers {
			c := &data.ContainerCharacteristic{
				Id: container.Id,
				IsReachable: container.IsReachable,
				Version: container.Version,
				Chars: container.getCharacteristic(),
			}
			ccs = append(ccs, c)
		}
		buf, _ := json.Marshal(ccs)
		ack := &members.MessagePkg{
			Type: pkg.Type,
			Ctx: pkg.Ctx,
			Msg: &members.Message{
				Path: msg.Path,
				Method: members.MsgMethodGet,
				State: "ok",
				Home: h.Id,
				Data: buf,
			},
		}
		h.replyMemberResult(ack)
	}
	return nil
}

// 属性控制操作
func (cs *ContainerManager) handleMemberCharacteristic(h *Home, pkg *members.MessagePkg) error {
	msg := pkg.Msg
	if msg.Method == members.MsgMethodPost {
		var actions []*Action
		err := json.Unmarshal(msg.Data, &actions)
		if err == nil {
			cs.executeActions(h, actions...)
		}
	}
	return nil
}

func (cs *ContainerManager) Handle(h *Home, pkg *members.MessagePkg) error {
	switch pkg.Msg.Path {
	case PathContainer:
		return cs.handleMemberContainer(h, pkg)
	case PathContainerCharacteristic:
		return cs.handleMemberContainerCharacteristic(h, pkg)
	case PathCharacteristic:
		return cs.handleMemberCharacteristic(h, pkg)
	}
	return nil
}